{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# select the same vscode's python environment as notebook kernel.\n",
    "\n",
    "# confirm import pytest succeeded and marked as accessed (from other cells)\n",
    "import pytest"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic hover in notebook context\n",
    "from hover import foo, MyNumber\n",
    "\n",
    "# hover on `foo`` and confirm signature and doc comment\n",
    "foo(10)\n",
    "\n",
    "# hover on `os` and confirm it shows tooltip for `os` module\n",
    "c = \"os\"\n",
    "\n",
    "# hover on `+` and confirm it shows tooltip for `__add__`\n",
    "myNumber = MyNumber(0) + MyNumber(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic completion in notebook context\n",
    "from completion import MyDict, MyType\n",
    "\n",
    "# bring up completion after `os` and confirm tooltip and `os` is added as `auto import` \n",
    "os\n",
    "\n",
    "d = { \"some key\": 1, \"another#2\": \"#2\" }\n",
    "d[\"#3 key\"] = 3\n",
    "\n",
    "# bring up regular dict completion inside of `[]` and confirm all keys are suggested\n",
    "d[]\n",
    "\n",
    "# bring up symbol completion after `My` and confirm tooltip and `MyDict` is suggested\n",
    "My\n",
    "\n",
    "# bring up symbol completion after `import` and confirm all top level modules are suggested\n",
    "import \n",
    "\n",
    "# bring up symbol completion after `import` and confirm all symbols under pandas \n",
    "# including sub modules are suggested\n",
    "from pandas import \n",
    "\n",
    "# bring up override completion after `me` and confirm `method` is suggested\n",
    "# commit the completion and confirm all necessary imports are inserted.\n",
    "class Derived(MyType):\n",
    "    def me"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic code action in notebook context\n",
    "\n",
    "# place cursor on `os` and confirm lightbulb shows up\n",
    "# and trigger quick fix and confirm `Add 'import os'` is listed\n",
    "os\n",
    "\n",
    "# place cursor on `userModule` and confirm lightbulb shows up\n",
    "# and trigger quick fix and confirm `Add 'from lib import userModule'` is listed\n",
    "userModule\n",
    "\n",
    "# place cursor on `MyType` and confirm lightbulb not shows up\n",
    "# since it is imported from cell above\n",
    "MyType()\n",
    "\n",
    "# place cursor on `sys` and confirm lightbulb shows up\n",
    "# and trigger quick fix and confirm `remove unused import` is listed\n",
    "# execute the code action and confirm it works as expected\n",
    "import sys\n",
    "\n",
    "# place cursor on `TypeToMove` and confirm lightbulb not shows up\n",
    "# since `move symbol` is not available in notebook\n",
    "class TypeToMove:\n",
    "    pass\n",
    "\n",
    "# place cursor on `outerModule` and confirm lightbulb shows up\n",
    "# and trigger quick fix and confirm `Add \"./outsideLib\" to extraPaths` is listed\n",
    "# execute the code action and confirm `extraPaths` is added to `settings.json`\n",
    "# Undo currently does not work: https://github.com/microsoft/pylance-release/issues/5161\n",
    "import outerModule\n",
    "\n",
    "# place cursor on `unknownModule` and confirm lightbulb shows up\n",
    "# and trigger quick fix and confirm `select a different kernel` and \n",
    "# `learn more about resolving import` entry is listed\n",
    "# execute the code action and confirm it works as expected\n",
    "import unknownModule\n",
    "\n",
    "# place cursor on `userModule` and confirm lightbulb not shows up\n",
    "# since `Convert to relative path` is not available in notebook\n",
    "from lib.userModule import ConvertImportPath\n",
    "\n",
    "# place cursor on `mailbox` and confirm lightbulb shows up\n",
    "# and trigger quick fix and confirm `Rename \"...mailbox\" to \"...mailbox_x\"` entry is listed\n",
    "# execute the code action and confirm it works as expected\n",
    "# Undo currently does not work: https://github.com/microsoft/pylance-release/issues/5162\n",
    "import mailbox\n",
    "\n",
    "\n",
    "# place cursor on `event` and confirm lightbulb shows up\n",
    "# and trigger quick fix and confirm `Create Type Stub` entry is listed\n",
    "# execute the code action and confirm it works as expected\n",
    "import zope.event"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic go to def in notebook context\n",
    "from gotodef import ClassWithMagicMethod\n",
    "\n",
    "# place cursor on `ClassWithMagicMethod` and `<` and issue go to def command\n",
    "a = ClassWithMagicMethod() < 1\n",
    "\n",
    "# place cursor on \"os\" and issue go to def command\n",
    "b = \"os\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic go to decl in notebook context\n",
    "\n",
    "# place cursor on `==` and issue go to decl command\n",
    "# it should go to pyi file.\n",
    "a = 1 == 1\n",
    "\n",
    "# place cursor on \"os\" and issue go to decl command\n",
    "# it should go to pyi file.\n",
    "b = \"os\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic go to type def in notebook context\n",
    "\n",
    "# place cursor on `myNumber` and run go to type def\n",
    "print(myNumber)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic document symbol in notebook context\n",
    "\n",
    "# Document symbols should show a concatentation of the list of symbols defined in each cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic document highlight in notebook context\n",
    "\n",
    "# Document highlight in notebooks currently highlights the symbol name in comments\n",
    "# also due to https://github.com/microsoft/vscode/issues/202293\n",
    "from typing import Optional\n",
    "\n",
    "# This is currently broken. Document highlight acts like string search instead of\n",
    "# semantic search -- https://github.com/microsoft/pylance-release/issues/5706\n",
    "\n",
    "# place cursor on `variable` and confirm all `variable` referenced in the document\n",
    "# is highlighted\n",
    "variable = \"Hello\"\n",
    "\n",
    "print(variable)\n",
    "\n",
    "# place cursor on `ch` and confirm the same\n",
    "for ch in variable:\n",
    "    print(ch)\n",
    "\n",
    "\n",
    "# place cursor on `ConstructorHR` and confirm all references are highlighted\n",
    "class ConstructorHR:\n",
    "    # place cursor on `__init__` and confirm all references of object creation are highlighted\n",
    "    def __init__(self, title = \"title\", sep: Optional[int] = None):\n",
    "        pass\n",
    "\n",
    "def foo(i: ConstructorHR) -> ConstructorHR:\n",
    "    return ConstructorHR()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic signature help in notebook context\n",
    "\n",
    "# place cursor between `()` and issue the command\n",
    "# confirm the tooltip and overload and active parameter (bold)\n",
    "ConstructorHR()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic format on type in notebook context\n",
    "\n",
    "for a in range(10):\n",
    "\n",
    "__break_for_statement_ # its here so that code after this is not recognized as body of the for statement.\n",
    "\n",
    "# place cursor after `\"a\"` and hit enter \n",
    "ch = \"a\"\n",
    "\n",
    "# place cursor after `ch:` and hit enter \n",
    "match ch:\n",
    "    # place cursor after `:` and hit enter\n",
    "    case \"a\":\n",
    "\n",
    "__break_for_case_ # its here so that code after this is not recognized as body of the case statement.\n",
    "\n",
    "if ch == \"a\":\n",
    "    pass\n",
    "    # type `:` after `else` and see `else` is moved to right position.\n",
    "    else"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic folding range in notebook context\n",
    "\n",
    "# Folding this function should only leave the `def` line visible. The line\n",
    "# with the `age` parameter will be hidden along with the function body.\n",
    "def functionWithArgsSpreadOverMultipleLines(name: str,\n",
    "                                            age: int):\n",
    "    pass\n",
    "\n",
    "# Folding this class should leave only the `class` line visible.\n",
    "class Class:\n",
    "    # Single-line ranges (such as single-line functions) are not foldable.\n",
    "    def singleLineFunction(self): pass\n",
    "\n",
    "    def function1(self, name: str, address: str, title: str):\n",
    "        # Multiline strings can be folded, leaving only the first line visible.\n",
    "        \"\"\"Doc string\n",
    "        Doc string\n",
    "        \"\"\"\n",
    "        pass\n",
    "\n",
    "# If a `#region` comment has a matching `#endregion` comment, folding the\n",
    "# `#region` will collapse everything from the `#region` to the matching\n",
    "# `#endregion` leaving just the `#region` line visible. Note that regions\n",
    "# can be nested so the matching `#endregion` is not necessarily the next one.\n",
    "\n",
    "#region foo1\n",
    "def foo1(): pass #region not on its own line is ignored\n",
    "# region foo2\n",
    "def foo2(): pass\n",
    " #region foo3\n",
    "def foo3(): pass\n",
    "# endregion\n",
    " #endregion\n",
    "def foo4(): pass\n",
    "#endregion with text after"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic selection range in notebook context\n",
    "\n",
    "from typing import Literal\n",
    "\n",
    "def foo(ch: Literal[\"a\", \"b\", \"c\"]):\n",
    "    match ch:\n",
    "        case \"a\":\n",
    "            pass\n",
    "        case \"b\":\n",
    "            for i in range(10):\n",
    "                # place cursor at `print` and issue `Expand Selection`\n",
    "                # repeat the command and confirm the selection is expanded as expected\n",
    "                print(f\"{ch}{i}\")\n",
    "        case \"c\":\n",
    "            pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic semantic token in notebook context\n",
    "\n",
    "from typing import Generic, TypeVar\n",
    "\n",
    "# confirm identifier, keyword, operators and etc are all colored as expected.\n",
    "T = TypeVar(\"T\", int, float)\n",
    "\n",
    "class Box(Generic[T]):\n",
    "    def __init__(self, v: T):\n",
    "        self._value = v\n",
    "\n",
    "    def __add__(self, v: \"Box[T]\") -> \"Box[T]\":\n",
    "        return Box(self.value + v.value)\n",
    "\n",
    "    def __eq__(self, value: T) -> bool:\n",
    "        return self.value == value\n",
    "    \n",
    "    @property\n",
    "    def value(self):\n",
    "        return self._value\n",
    "    \n",
    "a = Box(10)\n",
    "b = Box(20)\n",
    "c = a + b\n",
    "\n",
    "d = 1 + 2\n",
    "\n",
    "if c == d:\n",
    "    print(\"Yay\")\n",
    "else:\n",
    "    print(\"hmmm\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic inlay hint in notebook context\n",
    "\n",
    "from io import FileIO\n",
    "from pathlib import Path\n",
    "\n",
    "# confirm inlay return type\n",
    "def method(a: int, b: str, /, c: Path, *, d: Optional[FileIO] = None):\n",
    "    return a\n",
    "\n",
    "# confirm inlay variable type and call arguments\n",
    "var = method(10, \"hello\", Path(\"path\"), d=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic diagnostics in notebook context\n",
    "\n",
    "# import error\n",
    "import unknownModule\n",
    "\n",
    "# unknown identifier (semantic/checker error)\n",
    "unknownIdentifier\n",
    "\n",
    "# syntax error\n",
    ":\"\"\n",
    "\n",
    "# type error (semantic/checker error)\n",
    "# if you hover your mouse on the error, you should be able to execute code action\n",
    "# associated with the error explicitly. it can be done from problem tab as well by \n",
    "# hovering icon on the entry in problem tab.\n",
    "a: int = \"Hello\"\n",
    "\n",
    "# unused import fading out\n",
    "# these won't show up in problem tab\n",
    "import lxml\n",
    "\n",
    "def foo():\n",
    "    # unused symbol fading out\n",
    "    # these won't show up in problem tab\n",
    "    a = 1\n",
    "\n",
    "if False:\n",
    "    # unreachable code fading out\n",
    "    # these won't show up in problem tab\n",
    "    print(\"unreachable code\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic find all references in notebook context\n",
    "\n",
    "# put cursor on `Path` and run find all references\n",
    "from zipfile import Path as zipPath\n",
    "from lib.userModule import Derived\n",
    "\n",
    "class FindConstructor:\n",
    "    # put cursor on `__init__` and run find all references\n",
    "    # and confirm all object instantiation expressions are found as well\n",
    "    # such as FindConstructor()\n",
    "    def __init__(self) -> None:\n",
    "        pass\n",
    "\n",
    "c = FindConstructor()\n",
    "\n",
    "d = Derived()\n",
    "\n",
    "# put cursor on `method` and run find all references\n",
    "# and confirm that it found all overriden methods\n",
    "d.method()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic rename in notebook context\n",
    "\n",
    "# put cursor on `MyType` and rename it to `MyType2`\n",
    "# make sure undo work as well.\n",
    "m = MyType()\n",
    "\n",
    "# put cursor on `method` and rename it to `method2`\n",
    "# make sure undo work as well.\n",
    "m.method(v=zipPath(\"path\"))\n",
    "\n",
    "class RenameConstructor:\n",
    "    # put cursor on `__init__` and rename\n",
    "    # make sure it doesn't rename anything else but itself\n",
    "    def __init__(self) -> None:\n",
    "        pass\n",
    "\n",
    "c = RenameConstructor()\n",
    "\n",
    "d = Derived()\n",
    "\n",
    "# put cursor on `method` and rename it to `method2`\n",
    "# and confirm it renamed all overriden methods\n",
    "d.method()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic go to symbol in notebook context\n",
    "\n",
    "from typing import TypedDict\n",
    "\n",
    "# run `Go to symbols in workspace` and type `NotebookDict` and verify it finds all\n",
    "# `NotebookDict` type defined\n",
    "class NotebookDict(TypedDict):\n",
    "    pass\n",
    "\n",
    "# run `Go to symbols in workspace` and type `notebookVar` and verify it finds all\n",
    "# symbol that has `notebookVar` in them\n",
    "notebookVar = 1\n",
    "\n",
    "# run `Go to symbols in workspace` and type `note` and verify it finds all\n",
    "# symbol that has `notebook` in them\n",
    "def notebookFoo():\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic call hierarchy in notebook context\n",
    "\n",
    "import showCallHierarchy as c\n",
    "\n",
    "d = c.DerivedConsumer()\n",
    "\n",
    "# run `call hierarchy` on `consumer` and `method` and confirm caller and callee\n",
    "d.consumer(c.Derived()).method()\n",
    "\n",
    "# run `call hierarchy` on `Class` and `function1` and confirm caller and callee\n",
    "Class().function1(\"\", \"\", \"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic type hierarchy in notebook context\n",
    "\n",
    "from lib.userModule import Base, Derived2, Derived3\n",
    "\n",
    "# place cursor on `Derived3` and see type hierarchy\n",
    "# check supertypes\n",
    "a = Derived3()\n",
    "\n",
    "# place cursor on `method` and see type hierarchy\n",
    "# check supertypes\n",
    "a.method()\n",
    "\n",
    "# place cursor on `method` and see type hierarchy\n",
    "# check subtypes and supertypes\n",
    "b = Box(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic file watcher in notebook context\n",
    "\n",
    "# open `src/lib/changeExternally.py` in notepad and follow the instruction\n",
    "# and confirm the error below go away\n",
    "# make sure `changeExternally` file is not opened in vscode\n",
    "from lib.changeExternally import TypeChangedExternally\n",
    "\n",
    "TypeChangedExternally()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic trusted workspace in notebook context\n",
    "\n",
    "# open `Workspace Trust` window and put vscode in `restricted mode`\n",
    "# and confirm `pytest` and anything under it is no longer resolved.\n",
    "# but one we bundled is still working\n",
    "# you can confirm by hover `Path` and `Config` and etc\n",
    "# after that, put vscode back into `trusted mode` and confirm everything works again.\n",
    "c = pytest.Cache(Path(\"path\"), pytest.Config(pytest.PytestPluginManager()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test basic rename files in notebook context\n",
    "\n",
    "# you can trigger rename files by renaming this file from vscode explorer tab\n",
    "\n",
    "# rename `renameFolders` to `renameFolders1` using `Rename` command and confirm all references\n",
    "# are updated properly and confirm undo work as expected\n",
    "import renameFolders\n",
    "\n",
    "# rename `renameByModule` to `renameByModule1` using `Rename` command and confirm all references\n",
    "# are updated properly and confirm undo work as expected\n",
    "import renameFolders.renameByModule"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# change notebook kernel to different one and confirm notebook doesn't crash \n",
    "# and notebook react accordingly. (some imports are no longer resolved and semantic/inlay refreshs as expected)\n",
    "\n",
    "# Currently broken because we're not notified of kernel changes: https://github.com/microsoft/vscode/issues/199800"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
